package org.easy.eao.spring.jpa;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import javax.persistence.TemporalType;

import org.apache.commons.lang.StringUtils;
import org.easy.eao.annotations.Named;
import org.easy.eao.annotations.Query;
import org.easy.eao.annotations.Query.FirstResult;
import org.easy.eao.annotations.Query.MaxResults;
import org.easy.eao.annotations.jpa.Temporal;
import org.easy.eao.exception.ActionInitializeException;
import org.easy.eao.utils.Parameter;
import org.easy.eao.utils.ReflectUtils;
import org.springframework.orm.jpa.JpaCallback;

/**
 * 查询动作实现类
 * @author Frank
 */
public class QueryAction extends JpaActionSupport<Query> {
	
	/**	返回类型 */
	private Class<?> returnType;
	
	/** 开始返回位置索引值 */
	private Integer firstResultIdx;
	/** 最大返回记录数索引值 */
	private Integer maxResultsIdx;
	/** 位置参数 */
	private List<Parameter> indexParams = new ArrayList<Parameter>(0);
	/** 命名参数 */
	private List<Parameter> namedParams = new ArrayList<Parameter>(0);
	
	@Override
	public void initialize(Annotation annotation, Method method, Class<?> clz) {
		super.initialize(annotation, method, clz);
		this.returnType = method.getReturnType();
		initizlizeParameters();
	}
	
	/**
	 * 初始化查询参数设置
	 * @param method
	 */
	private void initizlizeParameters() {
		Parameter[] parameters = ReflectUtils.getParameter(method);
		for (Parameter p : parameters) {
			if (p.hasAnnotation(Temporal.class)
					&& (Date.class.isAssignableFrom(p.getClass()) || Calendar.class.isAssignableFrom(p.getClass())))
				throw new ActionInitializeException("参数类型与注释声明要求不符");
			if (p.hasAnnotation(FirstResult.class)) {
				firstResultIdx = p.getIndex();
				continue;
			}
			if (p.hasAnnotation(MaxResults.class)) {
				maxResultsIdx = p.getIndex();
				continue;
			}
			if (p.hasAnnotation(Named.class)) {
				namedParams.add(p);
				continue;
			}
			indexParams.add(p);
		}
	}
	
	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object execute(final Object[] parameters) {
		return getJpaTemplate().execute(new JpaCallback() {
			@Override
			public Object doInJpa(EntityManager em) throws PersistenceException {
				javax.persistence.Query query = getQuery(em, method);
				fittingQuery(query, parameters);
				return executeQuery(query, method);
			}
		});
	}

	/**
	 * 执行查询，更具不同的返回类型，以不同的查询方式执行
	 * @param query
	 * @param method
	 * @return
	 */
	protected Object executeQuery(javax.persistence.Query query, Method method) {
		if (List.class.isAssignableFrom(returnType))
			return query.getResultList();
		if (Iterator.class.isAssignableFrom(returnType))
			return query.getResultList().iterator();
		try {
			return query.getSingleResult();
		} catch (NoResultException ex) {
			return null;
		}
	}

	/**
	 * 填充查询参数
	 * @param query
	 * @param parameters
	 */
	protected void fittingQuery(javax.persistence.Query query, Object[] parameters) {
		// Limit 设置
		if (firstResultIdx != null)
			query.setFirstResult((Integer) parameters[firstResultIdx]);
		if (maxResultsIdx != null)
			query.setMaxResults((Integer) parameters[maxResultsIdx]);
		// 按位置设置参数
		for (int i = 0; i < indexParams.size(); i++) {
			Parameter p = indexParams.get(i);
			if (p.hasAnnotation(Temporal.class)) {
				Temporal anno = p.getAnnotation(Temporal.class);
				TemporalType type = TemporalType.valueOf(anno.value());
				if (parameters[p.getIndex()] instanceof Date)
					query.setParameter(i + 1, (Date) parameters[p.getIndex()], type);
				else
					query.setParameter(i + 1, (Calendar) parameters[p.getIndex()], type);
			} else {
				query.setParameter(i + 1, parameters[p.getIndex()]);
			}
		}
		// 按名字设置参数
		for (Parameter p : namedParams) {
			Named named = p.getAnnotation(Named.class);
			if (p.hasAnnotation(Temporal.class)) {
				Temporal anno = p.getAnnotation(Temporal.class);
				TemporalType type = TemporalType.valueOf(anno.value());
				if (parameters[p.getIndex()] instanceof Date)
					query.setParameter(named.value(), (Date) parameters[p.getIndex()], type);
				else
					query.setParameter(named.value(), (Calendar) parameters[p.getIndex()], type);
			} else {
				query.setParameter(named.value(), parameters[p.getIndex()]);
			}
		}
	}

	/**
	 * 获取要执行的查询对象
	 * @param session
	 * @param method
	 * @return
	 */
	protected javax.persistence.Query getQuery(EntityManager em, Method method) {
		if (StringUtils.isNotBlank(annotation.named()))
			return em.createNamedQuery(annotation.named());
		else
			return em.createQuery(annotation.query());
	}

}
